package io.github.source;


import com.orhanobut.logger.Logger;

import java.util.ArrayList;
import java.util.List;

import io.github.source.bean.Channel;
import io.github.source.media.bean.MediaPlay;
import io.github.source.media.bean.extractor.BiliBili;
import io.github.source.media.bean.extractor.Force;
import io.github.source.media.bean.extractor.JianPian;
import io.github.source.media.bean.extractor.Push;
import io.github.source.media.bean.extractor.TVBox;
import io.github.source.media.bean.extractor.TVBus;
import io.github.source.media.bean.extractor.Video;
import io.github.source.media.bean.extractor.ZLive;
import io.github.source.util.UrlUtil;

/**
 * 该类 Source 用于管理多个视频提取器（Extractor），并根据 URL 选择合适的提取器来解析视频源。
 */
public final class Source {

    private final List<Extractor> extractors;

    private static class Loader {
        static volatile Source INSTANCE = new Source();
    }

    public static Source get() {
        return Loader.INSTANCE;
    }

    public Source() {
        extractors = new ArrayList<>();
        extractors.add(new BiliBili());
        extractors.add(new Force());
        extractors.add(new JianPian());
        extractors.add(new Push());
//        extractors.add(new Thunder());
        extractors.add(new TVBus());
        extractors.add(new TVBox());
        extractors.add(new Video());
//        extractors.add(new Youtube());
        extractors.add(new ZLive());
    }

    private Extractor getExtractor(String url) {
        String host = UrlUtil.host(url);
        String scheme = UrlUtil.scheme(url);
        for (Extractor extractor : extractors) if (extractor.match(scheme, host)) return extractor;
        return null;
    }

    public String fetch(MediaPlay play) throws Exception {
        if (play == null || play.getUrl() == null) {
            Logger.w("Invalid MediaPlay or URL is null");
            return "";
        }
        String rawUrl = play.getUrl().v();
        if (rawUrl.isEmpty()) {
            Logger.w("Raw URL is empty");
            return "";
        }

        try {
            Extractor extractor = getExtractor(rawUrl);
            Logger.d("Using extractor: %s for URL: %s", extractor, rawUrl);

            if (extractor != null) {
                if (extractor instanceof Video) {
                    play.setParse(1); // 1=解析链接
                } else {
                    play.setParse(0);  // 0不解析链接
                }
                return extractor.fetch(rawUrl);
            } else {
                Logger.w("No extractor found for URL: %s", rawUrl);
                return rawUrl;
            }
        } catch (Exception e) {
            Logger.e("Error fetching URL: %s", e, rawUrl);
            return rawUrl; // 或者返回空字符串，视业务需求而定
        }


    }

    public boolean needParse() {
        return false;
    }

    /*频道加载*/
    public String fetch(Channel channel) throws Exception {
        String url = channel.getCurrent().split("\\$")[0];
        Extractor extractor = getExtractor(url);
        if (extractor != null) channel.setParse(0);
        if (extractor instanceof Video) channel.setParse(1);
        return extractor == null ? url : extractor.fetch(url);
    }

    public void stop() {
        if (extractors == null) return;
        for (Extractor extractor : extractors) extractor.stop();
    }

    public void exit() {
        if (extractors == null) return;
        for (Extractor extractor : extractors) extractor.exit();
    }

    /**
     * Extractor接口定义了一个用于从URL中提取信息的规范。
     * 它包含了匹配URL、从URL获取信息、停止操作和退出程序的方法。
     */
    public interface Extractor {

        /**
         * 判断给定的URL是否符合特定的模式和主机名条件。
         *
         * @param scheme URL的协议部分，如"http"或"https"。
         * @param host   URL的主机部分，如"www.example.com"。
         * @return 如果URL匹配则返回true，否则返回false。
         */
        boolean match(String scheme, String host);

        /**
         * 从给定的URL中提取信息。
         *
         * @param url 需要提取信息的URL字符串。
         * @return 从URL中提取到的信息字符串。
         * @throws Exception 如果在提取信息的过程中发生错误。
         */
        String fetch(String url) throws Exception;

        /**
         * 停止当前的提取操作。
         * 此方法用于中断正在进行的信息提取过程。
         */
        void stop();

        /**
         * 退出程序。
         * 此方法用于终止程序的执行，可能是为了释放资源或结束提取过程。
         */
        void exit();
    }

}
